#include <memory>
#include "BSException/BSException.h"
#include "Log/SystemLog.h"
#include "SharedMemory.h"

using namespace SystemTool::Buffer;

// Construct the shared memory with a fixed size and name
SharedMemory::SharedMemory() :
	m_pManagedMemory(NULL),
	m_strMemoryName("BASE_TOOL_SHM"),
	m_iMemorySize(64*1024),
	m_Disposed(false)
{
	Initialize();
}

// Detructe the SharedMemory
SharedMemory::~SharedMemory()
{
	Destory();
}

// Initialize the shared memory
SharedMemory::None SharedMemory::Initialize()
{

}

// Dispose the shared memory
SharedMemory::None SharedMemory::Destory()
{
	if (!GetDisposed())
	{
		SetDisposed(true);

		Close();
	}
}

// Configure the memory
SharedMemory::None SharedMemory::Configure(const std::string strMemoryName, const int32_t iMemorySize)
{
	if (!strMemoryName.empty())
	{
		SetMemoryName(strMemoryName);
	}
	
	if (iMemorySize > 1)
	{
		SetMemorySize(iMemorySize);
	}
}

// Is the shared memory opened or not
SharedMemory::Boolean SharedMemory::IsOpen()
{
	if (GetManagedMemory())
	{
		return true;
	}

	return false;
}

// Destory the share memory
SharedMemory::Boolean SharedMemory::DestoryMemory()
{
	if (!bip::shared_memory_object::remove(GetMemoyName().c_str()))
	{
		std::string strMsg = std::string("Failed to remove the memory:") + GetMemoyName();

		LOG_ERROR_EX(strMsg);

		return false;
	}
	
	std::string strMsg = std::string("Successfully to remove the memory:") + GetMemoyName();

	LOG_ERROR_EX(strMsg);

	return true;
}

// Open the map file
SharedMemory::Boolean SharedMemory::Open(SmOpenMode eOpenMode)
{
	try
	{
		if (eOpenMode == SM_CREATE_ONLY)
		{
			// Remove the memory at first
			DestoryMemory();

			SetManagedMemory(new bip::shared_memory_object(bip::create_only, GetMemoyName().c_str(), bip::read_write));

			GetManagedMemory()->truncate(GetMemorySize());
		}
		else if (eOpenMode == SM_OPEN_ONLY)
		{
			SetManagedMemory(new bip::shared_memory_object(bip::open_only, GetMemoyName().c_str(), bip::read_write));
		}
	}
	catch (boost::interprocess::interprocess_exception & e)
	{
		SetManagedMemory(NULL);

		int iErrorCode = e.get_error_code();

		std::string strMsg = std::string("Failed to create or open share memory,error code:")
			+ std::to_string(iErrorCode)
			+ " error msg:"
			+ e.what();

		LOG_ERROR_EX(strMsg);

		return false;
	}

	return true;
}

// Close the map file
SharedMemory::Boolean SharedMemory::Close()
{
	if (GetManagedMemory())
	{
		delete GetManagedMemory();

		SetManagedMemory(NULL);
	}

	return true;
}

// Clear the memory
SharedMemory::Boolean SharedMemory::Clear()
{
	bip::shared_memory_object* pMemory = GetManagedMemory();

	if (pMemory == NULL)
	{
		LOG_ERROR_EX("Share memory is not opened yeap");

		return false;
	}

	try
	{
		bip::mapped_region mmap(*pMemory, bip::read_only);

		char* pMmpData = static_cast<char*>(mmap.get_address());

		size_t iMmpDataSize = mmap.get_size();

		std::memset(pMmpData, 0, iMmpDataSize);
	}
	catch (boost::interprocess::interprocess_exception & e)
	{
		int iErrorCode = e.get_error_code();

		std::string strMsg = std::string("Failed to read data from share memory,error code:")
			+ std::to_string(iErrorCode)
			+ " error msg:"
			+ e.what();

		LOG_ERROR_EX(strMsg);

		return false;
	}

	return true;
}

// Read the common memory.
// (Len must be one of 64K ,2*64K,4*64KIt can be called one-level cache,two-level cache and four -level cache)
SharedMemory::Boolean SharedMemory::Read(ByteArray buffer, Length offset, Length len)
{
	if (buffer == NULL)
	{
		LOG_ERROR_EX("Read buffer is null");

		return false;
	}

	if (len <= 0)
	{
		LOG_ERROR_EX("Read size must > 0");

		return false;
	}

	if (offset < 0 || offset >= len)
	{
		LOG_ERROR_EX("Read buffer's offset is not in the range of buffer size");

		return false;
	}

	bip::shared_memory_object* pMemory = GetManagedMemory();

	if (pMemory == NULL)
	{
		LOG_ERROR_EX("Share memory is not opened yeap");

		return false;
	}

	try
	{
		bip::mapped_region mmap(*pMemory, bip::read_only);

		char* pMmpData = static_cast<char*>(mmap.get_address());

		size_t iMmpDataSize = mmap.get_size();

		std::copy(pMmpData, pMmpData + len, buffer + offset);
	}
	catch (boost::interprocess::interprocess_exception & e)
	{
		int iErrorCode = e.get_error_code();

		std::string strMsg = std::string("Failed to read data from share memory,error code:")
			+ std::to_string(iErrorCode)
			+ " error msg:"
			+ e.what();

		LOG_ERROR_EX(strMsg);

		return false;
	}

	return true;
}

// Write the map file
SharedMemory::Boolean SharedMemory::Write(ByteArray buffer, Length offset, Length len)
{
	if (buffer == NULL)
	{
		LOG_ERROR_EX("Write buffer is null");

		return false;
	}

	if (len <= 0)
	{
		LOG_ERROR_EX("Write size must > 0");

		return false;
	}

	if (offset < 0 || offset >= len)
	{
		LOG_ERROR_EX("Write buffer's offset is not in the range of buffer size");

		return false;
	}

	bip::shared_memory_object* pMemory = GetManagedMemory();

	if (pMemory == NULL)
	{
		LOG_ERROR_EX("Share memory is not opened yeap");

		return false;
	}

	try
	{
		bip::mapped_region mmap(*pMemory, bip::read_write);

		size_t iMmpDataSize = mmap.get_size();

		if (len > iMmpDataSize)
		{
			LOG_ERROR_EX("Write size > share memory total size");

			return false;
		}

		char* pMmpData = static_cast<char*>(mmap.get_address());

		std::copy(buffer + offset, buffer + len, pMmpData);
	}
	catch (boost::interprocess::interprocess_exception & e)
	{
		int iErrorCode = e.get_error_code();

		std::string strMsg = std::string("Failed to write data to share memory,error code:")
			+ std::to_string(iErrorCode)
			+ " error msg:"
			+ e.what();

		LOG_ERROR_EX(strMsg);

		return false;
	}

	return true;
}
